#include<iostream>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>

#define MAXARGC 1000
#define shell_prompt "[%s@%s %s]ƪ(˘⌣˘)ʃ优雅# "


//shell的全局变量 
//命令行参数表
char* argv[MAXARGC];
int argc;

//当前工作目录
char cwd[1024];
char cwdenv[1024];

//返回码
int lastcode;

//重定向
#define NONE_REDIR 0
#define INPUT_REDIR 1
#define OUTPUT_REDIR 2
#define APPEND_REDIR 3

int redir;
std::string redir_name;




const char* GetUserName()
{
  const char* name = getenv("USER");
  return name == NULL ? "???" : name;
}


const char* GetPWD()
{
  const char* pwd = getcwd(cwd, sizeof(cwd));
  if(pwd != NULL)
  {
    snprintf(cwdenv, sizeof(cwdenv), "PWD=%s", pwd);
    putenv(cwdenv);
  }
  return pwd == NULL ? "???" : pwd;
}


const char* GetHostName()
{
  const char* hname = getenv("HOSTNAME");
  return hname == NULL ? "???" : hname;
}


const char* GetHome()
{
  const char* home = getenv("HOME");
  return home == NULL ? "" : home;
}


bool GetCommandLine(char cmdl[], int size)
{
  char* c = fgets(cmdl, size, stdin);
  if(c == NULL) return false;
  cmdl[strlen(cmdl) - 1] = 0; // 去掉命令行最后的\n
  if(strlen(cmdl) == 0) return false;
  return true;
}


bool CommandPrase(char cmd_l[])
{
#define FLG " "
  argc = 0;
  argv[argc++] = strtok(cmd_l, FLG);
  while((bool)(argv[argc++] = strtok(NULL, FLG)));
  argc--;//修正
  return argc > 0 ? true : false;
}


void Execute()
{
  pid_t pi = fork();
  if(pi == 0)
  {
    //child
    if(redir == INPUT_REDIR)
    {
     int fd = open(redir_name.c_str(), O_RDONLY); 
     if(fd < 0) exit(1);
     dup2(fd, 0);
     close(fd);
    }
    else if(redir == OUTPUT_REDIR)
    {
      int fd = open(redir_name.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666);
      if(fd < 0) exit(1);
      dup2(fd, 1);
      close(fd);
    }
    else if(redir == APPEND_REDIR)
    {
      int fd = open(redir_name.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666);
      if(fd < 0) exit(1);
      dup2(fd, 1);
      close(fd);
    }
    else 
    {}
    execvp(argv[0],argv);
    exit(1);
  }
  //parent
 int status = 0;
 pid_t rid =  waitpid(-1, &status, 0);
 if(rid > 0)
 {
   lastcode = WEXITSTATUS(status);
 }
}


std::string DirName(const char* dir)
{
#define N_FLAG "/"
  std::string name = dir;
  if(name == "/") return "/";
  size_t pos = name.rfind(N_FLAG);
  return name.substr(pos + 1);
}


void MakeCommandPrompt(char cmd_pro[], int size)
{
  snprintf(cmd_pro, size, shell_prompt, GetUserName(), GetHostName(), DirName(GetPWD()).c_str());
}


void PrintCommandPrompt()
{
  char cmd_pro[128];
  MakeCommandPrompt(cmd_pro, sizeof(cmd_pro));
  printf("%s", cmd_pro);
  fflush(stdout);
}


bool Cd()
{
  if(argc == 1)
  {
    //默认更改至家目录
    std::string home = GetHome();
    if(home.empty()) return false;
    chdir(home.c_str());
    return true;
  }
  else 
  {
    std::string where = argv[1];
    int flg = chdir(where.c_str());
    if(!~flg) std::cout<<"dir not found !"<<std::endl;
    return true;
  }
}


void Echo()
{
  if(argc == 2)
  {
    std::string opt = argv[1];
    // echo "123"
    // echo $?  
    // echo $PATH ...
    if(opt == "$?")
    {
      std::cout<<lastcode<<std::endl; 
      lastcode = 0;
    }
    else if(opt[0] == '$')
    {
      std::string env_name = opt.substr(1);
      const char* env_val = getenv(env_name.c_str());
      if(env_val != NULL)
        std::cout<<env_name<<"="<<env_val<<std::endl;
    }
    else 
    {
      std::cout<<opt<<std::endl;
    }
  } 
}


bool CheckAndExecBuiltin()
{
  std::string cmd = argv[0];
  if(cmd == "cd")
  {
    Cd();
    return true;
  }
  else if(cmd == "echo")
  {
    Echo();
    return true;
  }
  return false;
}


void TrimSpace(char cmd[], int& end)
{
  while(isspace(cmd[end])) 
  {
    end++;
  }
}


void RedirCheck(char cmd[])
{
  redir = NONE_REDIR;
  redir_name.clear();
  // ls -a -l > xxx.text
  // ls -a -l >> xxx.text
  // ls -a -l < xxx.text
  int start = 0;
  int end = strlen(cmd) - 1;
  while(start < end)
  {
    if(cmd[end] == '<')
    {
      // <
      cmd[end++] = 0;
      redir = INPUT_REDIR;
      TrimSpace(cmd, end);
      redir_name = cmd + end;
      break;
    }
    else if(cmd[end] == '>')
    {
      if(cmd[end - 1] == '>')
      {
        // >>
        cmd[end - 1] = 0;
        redir = APPEND_REDIR;
      }
      else 
      {
        // >
        redir = OUTPUT_REDIR;
      }
        cmd[end++] = 0;
        TrimSpace(cmd, end);
        redir_name = cmd + end;
        break;
    }
    else 
    {
      end--;
    }
  }
}


int main()
{
  while(1)
  {
    //打印命令行信息
    PrintCommandPrompt();

    //输入命令
    char CommandLine[128];
    if(!GetCommandLine(CommandLine, sizeof(CommandLine))) continue;
    
    //重定向分析 -->修改重定向状态--->改变子进程文件列表的指针指向--->输入/输出/追加重定向
    RedirCheck(CommandLine);


    //命令行分析 放入argv中，使用argc来管理数量
    if(!CommandPrase(CommandLine)) continue;

    //判断内建命令
    if(CheckAndExecBuiltin()) continue; //在该函数内调用内建命令-->父进程调函数
    // for(int i = 0; argv[i]; i++) printf("%s\n",argv[i]);
   
    //子进程执行命令
    Execute();     

  }
  return 0;
}
